package org.je.dev.file;

import java.io.File;
import java.nio.ByteBuffer;

import org.je.io.IOUtil;
import org.je.io.RandomIO;
import org.je.log.JLog;
import org.je.util.NumberUtil;

/**
 * 前 cap个位置，每个位置占8个Byte， 即两个int,分别是文件存储的位置和大小
 * 
 * @author fanyifeng
 */
public class NumberedIO
{
	private RandomIO	io;
	private int			cap;
	private int[]		lctCache;

	/**
	 * @param path
	 */
	public NumberedIO(String path) {
		this(path ,100000);
	}

	public NumberedIO(String path, int cap) {
		this.io = new RandomIO(path);
		this.cap = cap;
		this.lctCache = new int[2 * cap];
		init();
	}

	/**
	 * @param idx
	 * @param s
	 */
	public void putStr(int idx, String s) {
		if (s == null) {
			return;
		}
		put(idx ,IOUtil.utf8(s));
	}

	public String getStr(int idx) {
		byte[] rst = get(idx);
		return rst == null ? null : IOUtil.utf8(rst);
	}

	/**
	 * @param n
	 * @param data
	 */
	public void put(int idx, byte[] data) {
		if (idx < 0 || idx >= cap) {
			throw new IndexOutOfBoundsException("cap: " + cap + ", idx:" + idx);
		}
		if (data == null || data.length == 0) {
			return;
		}
		synchronized (this) {
			int oldSize = (int)io.size();
			byte[] locater = new byte[8];
			System.arraycopy(NumberUtil.int2bytes(oldSize) ,0 ,locater ,0 ,4);
			System.arraycopy(NumberUtil.int2bytes(data.length) ,0 ,locater ,4 ,4);
			writeFully(locater ,idx * 8);
			writeFully(data ,oldSize);
			lctCache[idx * 2] = oldSize;
			lctCache[idx * 2 + 1] = data.length;
		}
	}

	public byte[] get(int idx) {
		if (idx < 0 || idx >= cap) {
			throw new IndexOutOfBoundsException("cap: " + cap + ", idx:" + idx);
		}
		synchronized (this) {
			int pos = lctCache[idx * 2];
			if (pos == 0) {
				return null;
			}
			int dsize = lctCache[idx * 2 + 1];
			if (dsize == 0) {
				return new byte[0];
			}
			byte[] b = new byte[dsize];
			readFully(b ,pos);
			return b;
		}
	}

	public long fileSize() {
		return io.size();
	}

	public int capacity() {
		return cap;
	}

	public void close() {
		io.close();
	}

	public void destroy() {
		io.close();
		new File(io.getPath()).delete();
	}

	private void init() {
		if (fileSize() < cap * 8) {
			writeFully(new byte[8] , (cap - 1) * 8);
			JLog.out("init-numbered-io: " + io.getPath());
		} else {
			byte[] b = new byte[cap * 8];
			readFully(b ,0);
			for (int i = 0; i < cap; ++i) {
				lctCache[i * 2] = NumberUtil.bytes2int(b ,i * 8);
				lctCache[i * 2 + 1] = NumberUtil.bytes2int(b ,i * 8 + 4);
			}
		}
	}

	private void writeFully(byte[] b, long pos) {
		ByteBuffer bf = ByteBuffer.wrap(b);
		long off = pos;
		int temp;
		while( bf.hasRemaining() ) {
			temp = io.write(bf ,off);
			if (temp >= 0) {
				off += temp;
			}
		}
	}

	private void readFully(byte[] b, long pos) {
		ByteBuffer bf = ByteBuffer.wrap(b);
		long off = pos;
		int temp;
		while( bf.hasRemaining() ) {
			temp = io.read(bf ,off);
			if (temp > -1) {
				off += temp;
			} else {
				throw new RuntimeException("read-negative,off: " + off);
			}
		}
	}
}
